1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.hiprenderer.renderer;
12 public import hip.config.renderer;
13 public import hip.hiprenderer.shader;
14 public import hip.hiprenderer.vertex;
15 public import hip.hiprenderer.framebuffer;
16 public import hip.hiprenderer.viewport;
17 public import hip.api.renderer.texture;
18 public import hip.api.renderer.operations;
19 public import hip.api.graphics.color;
20 public import hip.api.renderer.core;
21 public import hip.api.renderer.shadervar;
22 import hip.windowing.window;
23 import hip.math.rect;
24 import hip.error.handler;
25 import hip.console.log;
26 
27 
28 private struct HipRendererResources
29 {
30     IHipTexture[] textures;
31     Shader[] shaders;
32     IHipVertexArrayImpl[]  vertexArrays;
33     IHipRendererBuffer[] buffers;
34 }
35 
36 class HipRendererImplementation : IHipRenderer
37 {
38     static struct Statistics
39     {
40         ulong drawCalls;
41         ulong renderFrames;
42     }
43     protected Viewport currentViewport;
44     protected Viewport mainViewport;
45     protected IHipRendererImpl rendererImpl;
46     protected HipRendererMode rendererMode;
47     protected Statistics stats;
48     public  HipWindow window = null;
49     public  Shader currentShader;
50     package HipRendererType rendererType = HipRendererType.None;
51 
52     public uint width, height;
53     protected HipRendererConfig currentConfig;
54 
55     protected HipRendererResources res;
56     protected bool depthTestingEnabled;
57     protected HipDepthTestingFunction currentDepthTestFunction;
58 
59     protected IHipRendererBuffer quadIndexBuffer;
60 
61     public bool initialize (string confData, string confPath)
62     {
63         import hip.config.opts;
64         import hip.data.ini;
65         import hip.hiprenderer.initializer;
66         HipINI ini = HipINI.parse(confData, confPath);
67         HipRendererConfig cfg;
68         rendererType = getRendererTypeFromVersion();
69         int renderWidth = HIP_DEFAULT_WINDOW_SIZE[0];
70         int renderHeight = HIP_DEFAULT_WINDOW_SIZE[1];
71         string defaultRenderer = "OpenGL3";
72         version(AppleOS) defaultRenderer = "Metal";
73         if(!ini.configFound || !ini.noError)
74         {
75             import hip.util.string;
76             if(!ini.configFound)
77                 logln("No renderer.conf found");
78             if(!ini.noError)
79             {
80                 logln("Renderer.conf parsing error");
81                 rawerror(BigString(ini.errors).toString);
82             }
83             hiplog("Defaulting renderer to "~defaultRenderer);
84         }
85         else
86         {
87             cfg.bufferingCount = ini.tryGet!ubyte("buffering.count", 2);
88             cfg.multisamplingLevel = ini.tryGet!ubyte("multisampling.level", 0);
89             cfg.fullscreen = ini.tryGet("screen.fullscreen", false);
90             cfg.vsync = ini.tryGet("vsync.on", true);
91 
92             renderWidth = ini.tryGet("screen.width", renderWidth);
93             renderHeight = ini.tryGet("screen.height", renderHeight);
94             string renderer = ini.tryGet("screen.renderer", "GL3");
95             rendererType = rendererFromString(renderer);
96         }
97         return initialize(getRendererWithFallback(rendererType), &cfg, renderWidth, renderHeight);
98     }
99 
100     public Statistics getStatistics(){return stats;}
101     version(dll) public bool initExternal(HipRendererType type, int windowWidth = -1, int windowHeight = -1)
102     {
103         import hip.hiprenderer.initializer;
104         rendererType = type;
105         if(windowWidth == -1)
106             windowWidth = 1920;
107         if(windowHeight == -1)
108             windowHeight = 1080;
109         return initialize(getRendererWithFallback(type), null, cast(uint)windowWidth, cast(uint)windowHeight, true);
110     }
111 
112     private HipWindow createWindow(uint width, uint height)
113     {
114         HipWindow wnd = new HipWindow(width, height, HipWindowFlags.DEFAULT);
115         version(Android){}
116         else wnd.start();
117         return wnd;
118     }
119 
120     /**
121     *   Populates a buffer with indices forming quads
122     *   If the quadsCount is bigger than the existing one, throws since
123     *   it probably can be set at compile time and it is easier to control like that
124     */
125     public IHipRendererBuffer getQuadIndexBuffer(size_t quadsCount)
126     {
127         if(!quadIndexBuffer)
128         {
129             import hip.util.array;
130             quadIndexBuffer = createBuffer(quadsCount*index_t.sizeof*6, HipResourceUsage.Immutable, HipRendererBufferType.index);
131             index_t[] output = uninitializedArray!(index_t[])(quadsCount*6);
132             index_t index = 0;
133             for(index_t i = 0; i < quadsCount; i++)
134             {
135                 output[index+0] = cast(index_t)(i*4+0);
136                 output[index+1] = cast(index_t)(i*4+1);
137                 output[index+2] = cast(index_t)(i*4+2);
138 
139                 output[index+3] = cast(index_t)(i*4+2);
140                 output[index+4] = cast(index_t)(i*4+3);
141                 output[index+5] = cast(index_t)(i*4+0);
142                 index+=6;
143             }
144             quadIndexBuffer.setData(output);
145             import core.memory;
146             GC.free(output.ptr);
147         }
148 
149         return quadIndexBuffer;
150     }
151 
152     public bool initialize (IHipRendererImpl impl, HipRendererConfig* config, uint width, uint height, bool isExternal = false)
153     {
154         ErrorHandler.startListeningForErrors("Renderer initialization");
155         if(config != null)
156             currentConfig = *config;
157         currentConfig.logConfiguration();
158         rendererImpl = impl;
159         window = createWindow(width, height);
160         ErrorHandler.assertErrorMessage(window !is null, "Error creating window", "Could not create Window");
161         if(isExternal)
162         {
163             version(dll)
164             {
165                 if(!rendererImpl.initExternal())
166                 {
167                     ErrorHandler.showErrorMessage("Error Initializing Renderer", "Renderer could not initialize externally");
168                     return false;
169                 }
170             }
171         }
172         else
173             rendererImpl.init(window);
174         window.setVSyncActive(currentConfig.vsync);
175         window.setFullscreen(currentConfig.fullscreen);
176         window.show();
177         foreach(err; window.errors)
178             loglnError(err);
179 
180         setWindowSize(width, height);
181 
182         //After init
183         import hip.config.opts;
184         mainViewport = new Viewport(0,0, window.width, window.height);
185         setViewport(mainViewport);
186         setColor();
187         HipRenderer.setRendererMode(HipRendererMode.triangles);
188 
189         return ErrorHandler.stopListeningForErrors();
190     }
191     public void setWindowSize(int width, int height) @nogc
192     {
193         assert(width > 0 && height > 0, "Window width and height must be greater than 0");
194         logln("Changing window size to [", width, ", ",  height, "]");
195         window.setSize(cast(uint)width, cast(uint)height);
196         this.width  = width;
197         this.height = height;
198     }
199     public HipRendererType getType(){return rendererType;}
200 
201     /**
202      * Info is data that can't be changed from the renderer.
203      */
204     public HipRendererInfo getInfo()
205     {
206         return HipRendererInfo(
207             getType,
208             rendererImpl.getShaderVarMapper
209         );
210     }
211 
212     public HipRendererConfig getCurrentConfig(){return currentConfig;}
213     public int getMaxSupportedShaderTextures(){return rendererImpl.queryMaxSupportedPixelShaderTextures();}
214 
215 
216     public IHipTexture getTextureImplementation(HipResourceUsage usage = HipResourceUsage.Immutable)
217     {
218         res.textures~= rendererImpl.createTexture(usage);
219         return res.textures[$-1];
220     }
221 
222     public void setColor(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255)
223     {
224         rendererImpl.setColor(r,g,b,a);
225     }
226 
227     public Viewport getCurrentViewport() @nogc {return currentViewport;}
228     public void setViewport(Viewport v)
229     {
230         this.currentViewport = v;
231         v.updateForWindowSize(width, height);
232         rendererImpl.setViewport(v);
233     }
234 
235     public void reinitialize()
236     {
237         version(Android)
238         {
239             foreach(tex; res.textures)
240             {
241                 // (cast(Hip_GL3_Texture)tex).reload();
242             }
243             foreach(shader; res.shaders)
244             {
245                 shader.reload();
246             }
247         }
248     }
249 
250     public void setCamera()
251     {
252 
253     }
254     /**
255     * Fixes the matrix order based on the config and renderer.
256     * If the renderer is column and the config is row, it will tranpose
257     */
258     public T getMatrix(T)(auto ref T mat)
259     {
260         if(currentConfig.isMatrixRowMajor && !rendererImpl.isRowMajor())
261             return mat.transpose();
262         return mat;
263     }
264 
265     Shader newShader()
266     {
267         res.shaders~= new Shader(rendererImpl.createShader());
268         return res.shaders[$-1];
269     }
270     public Shader newShader(string vertexShaderPath, string fragmentShaderPath)
271     {
272         Shader ret = newShader();
273         ret.loadShadersFromFiles(vertexShaderPath, fragmentShaderPath);
274         return ret;
275     }
276 
277     public HipFrameBuffer newFrameBuffer(int width, int height, Shader frameBufferShader = null)
278     {
279         return new HipFrameBuffer(rendererImpl.createFrameBuffer(width, height), width, height, frameBufferShader);
280     }
281     public IHipVertexArrayImpl  createVertexArray()
282     {
283         res.vertexArrays~= rendererImpl.createVertexArray();
284         return res.vertexArrays[$-1];
285     }
286 
287     public IHipRendererBuffer createBuffer(size_t size, HipResourceUsage usage, HipRendererBufferType type)
288     {
289         res.buffers~= rendererImpl.createBuffer(size, usage, type);
290         return res.buffers[$-1];
291     }
292     public void setShader(Shader s)
293     {
294         currentShader = s;
295         s.bind();
296     }
297     public bool hasErrorOccurred(out string err, string file = __FILE__, size_t line =__LINE__)
298     {
299         return rendererImpl.hasErrorOccurred(err, file, line);
300     }
301 
302     public void exitOnError(string file = __FILE__, size_t line = __LINE__)
303     {
304         import hip.config.opts;
305         import core.stdc.stdlib:exit;
306         string err;
307         if(hasErrorOccurred(err, file, line))
308         {
309             loglnError(err, file,":",line);
310             static if(CustomRuntime)
311                 exit(-1);
312             else
313                 throw new Error(err);
314         }
315     }
316 
317     public void begin()
318     {
319 
320         rendererImpl.begin();
321     }
322 
323     public void setErrorCheckingEnabled(bool enable = true)
324     {
325         rendererImpl.setErrorCheckingEnabled(enable);
326     }
327 
328     public void setRendererMode(HipRendererMode mode)
329     {
330         if(mode != rendererMode)
331         {
332             rendererMode = mode;
333             rendererImpl.setRendererMode(mode);
334         }
335     }
336     public HipRendererMode getMode(){return rendererMode;}
337 
338     public void drawIndexed(index_t count, uint offset = 0)
339     {
340         rendererImpl.drawIndexed(count, offset);
341         stats.drawCalls++;
342     }
343     public void drawIndexed(HipRendererMode mode, index_t count, uint offset = 0)
344     {
345         setRendererMode(mode);
346         HipRenderer.drawIndexed(count, offset);
347         stats.drawCalls++;
348     }
349     public void drawVertices(index_t count, uint offset = 0)
350     {
351         rendererImpl.drawVertices(count, offset);
352     }
353     public void drawVertices(HipRendererMode mode, index_t count, uint offset = 0)
354     {
355         rendererImpl.setRendererMode(mode);
356         HipRenderer.drawVertices(count, offset);
357     }
358 
359     public void end()
360     {
361         rendererImpl.end();
362         foreach(sh; res.shaders) sh.onRenderFrameEnd();
363         stats.drawCalls=0;
364         stats.renderFrames++;
365     }
366     public void clear()
367     {
368         rendererImpl.clear();
369         stats.drawCalls++;
370     }
371     public void clear(HipColorf color)
372     {
373         auto rgba = color.unpackRGBA;
374         rendererImpl.clear(rgba[0], rgba[1], rgba[2], rgba[3]);
375         stats.drawCalls++;
376     }
377     public void clear(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255)
378     {
379         rendererImpl.clear(r,g,b,a);
380         stats.drawCalls++;
381     }
382     HipDepthTestingFunction getDepthTestingFunction() const
383     {
384         return currentDepthTestFunction;
385     }
386     bool isDepthTestingEnabled() const
387     {
388         return depthTestingEnabled;
389     }
390     void setDepthTestingEnabled(bool enable)
391     {
392         rendererImpl.setDepthTestingEnabled(enable);
393     }
394     void setDepthTestingFunction(HipDepthTestingFunction fn)
395     {
396         rendererImpl.setDepthTestingFunction(fn);
397         currentDepthTestFunction = fn;
398     }
399 
400     void setStencilTestingEnabled(bool bEnable)
401     {
402         rendererImpl.setStencilTestingEnabled(bEnable);
403     }
404     void setStencilTestingMask(uint mask)
405     {
406         rendererImpl.setStencilTestingMask(mask);
407     }
408     void setColorMask(ubyte r, ubyte g, ubyte b, ubyte a)
409     {
410         rendererImpl.setColorMask(r,g,b,a);
411     }
412     void setStencilTestingFunction(HipStencilTestingFunction passFunc, uint reference, uint mask)
413     {
414         rendererImpl.setStencilTestingFunction(passFunc, reference, mask);
415     }
416     void setStencilOperation(HipStencilOperation stencilFail, HipStencilOperation depthFail, HipStencilOperation stencilAndDephPass)
417     {
418         rendererImpl.setStencilOperation(stencilFail, depthFail, stencilAndDephPass);
419     }
420 
421     public void dispose()
422     {
423         rendererImpl.dispose();
424         if(window !is null)
425             window.exit();
426         window = null;
427     }
428 }
429 
430 
431 private __gshared HipRendererImplementation impl;
432 void PreInitializeHipRenderer()
433 {
434     impl = new HipRendererImplementation();
435     setHipRenderer(impl);
436 }
437 
438 
439 pragma(inline, true) HipRendererImplementation HipRenderer(){return impl;}
440 
441 export extern(System) IHipRenderer HipRendererAPI()
442 {
443     return HipRenderer;
444 }
445 
446 void logConfiguration(HipRendererConfig config)
447 {
448     import hip.console.log;
449     with(config)
450     {
451         loglnInfo("Starting HipRenderer with configuration: ",
452         "\nMultisamplingLevel: ", multisamplingLevel,
453         "\nBufferingCount: ", bufferingCount,
454         "\nFullscreen: ", fullscreen,
455         "\nVsync: ", vsync? "activated" : "deactivated");
456     }
457 }